/***************************************************************************/
/*                                                                         */
/* 234218 Data Structures 1, Spring 2011                                          */
/*                                                                         */
/* Homework : Wet 1                                                        */
/*                                                                         */
/***************************************************************************/

/***************************************************************************/
/*                                                                         */
/* File Name : main1.cpp                                                   */
/*                                                                         */
/* Holds the "int main()" function and the parser of the shell's           */
/* command line.                                                           */
/***************************************************************************/


#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "library1.h"
#include "rational.h"
#include <iomanip>
#include <iostream>
#include <sstream>

using namespace std;
using namespace rational;


/* The command's strings */
typedef enum {
  NONE_CMD = -2,
  COMMENT_CMD = -1,
  INIT_CMD = 0,
  HIRE_CMD = 1,
  FIRE_CMD = 2,
  GETEMPLOYER_CMD = 3,
  GETSALARY_CMD = 4,
  MEDIANSALARY_CMD = 5,
  HIGHESTSALARY_CMD = 6,
  UPDATESALARIES_CMD = 7,
  MERGER_CMD = 8,
  QUIT_CMD = 9
} commandType;



static const int   numActions   = 10;
static const char *commandStr[] = {
  "Init",
  "Hire",
  "Fire",
  "GetEmployer",
  "GetSalary",
  "MedianSalary",
  "HighestSalary",
  "UpdateSalaries",
  "Merger",
  "Quit"
};


static const char* ReturnValToStr(int val) {
	switch (val) {
		case (SUCCESS):          return "Success";
		case (FAILURE):          return "Failure";
		case (ALLOCATION_ERROR): return "Allocation_error";
		case (INVALID_INPUT):    return "Invalid_input";
		default:                 return "";
	}
}
	



/* we assume maximum string size is not longer than 256  */
#define MAX_STRING_INPUT_SIZE (255)
#define MAX_BUFFER_SIZE       (255)

#define StrCmp(Src1,Src2) ( strncmp((Src1),(Src2),strlen(Src1)) == 0 )

typedef enum {error_free, error} errorType;
static errorType parser(const char* const command);



#define ValidateRead(read_parameters,required_parameters,ErrorString) \
if ( (read_parameters)!=(required_parameters) ) { printf(ErrorString); return error; }


static bool isInit = false;


/* Print an array */
string PrintIntArray(const int* arr, int size) {
	char buffer[MAX_BUFFER_SIZE];
	string str = "";

	for (int i=0; i < size; i++) {
		sprintf(buffer,"%d",arr[i]);
		str += string(buffer) + ((i == (size - 1)) ? "" : ",");
	}
	return str.c_str();
}


/***************************************************************************/
/* main                                                                    */
/***************************************************************************/

int main(int argc, const char**argv) {
	FILE* inputFile = NULL;
	if(argc == 2){
		inputFile = fopen(argv[1], "r");
		if (inputFile == NULL){
			printf("Problem opening file %s, make sure correct path name is given. ", argv[1]);
			return 0;
		}
	}
	else {
	  inputFile = stdin;
	}
	
	char buffer[MAX_STRING_INPUT_SIZE];
	// Reading commands
	while ( fgets(buffer, MAX_STRING_INPUT_SIZE, inputFile) != NULL ) {
	fflush(stdout);
	if ( parser(buffer) == error )
		break;
	};
	fclose(inputFile);
	return 0;
}


/***************************************************************************/
/* Command Checker                                                         */
/***************************************************************************/

static commandType CheckCommand(const char* const command, const char** const command_arg) {
  if ( command == NULL || strlen(command) == 0 || StrCmp("\n", command) )
    return(NONE_CMD);
  if ( StrCmp("#", command) ) {
    if (strlen(command) > 1)
      printf("%s", command);
    return(COMMENT_CMD);
  };
  for (int index=0; index < numActions; index++) {
    if ( StrCmp(commandStr[index], command) ) {
      *command_arg = command + strlen(commandStr[index]) + 1;
      return((commandType)index);
    };
  };
  return(NONE_CMD);
}

/***************************************************************************/
/* Commands Functions                                                      */
/***************************************************************************/

static errorType OnInit(void** DS, const char* const command);
static errorType OnHire(void* DS, const char* const command);
static errorType OnFire(void* DS, const char* const command);
static errorType OnGetEmployer(void* DS, const char* const command);
static errorType OnGetSalary(void* DS, const char* const command);
static errorType OnMedianSalary(void* DS, const char* const command);
static errorType OnHighestSalary(void* DS, const char* const command);
static errorType OnUpdateSalaries(void* DS, const char* const command);
static errorType OnMerger(void* DS, const char* const command);
static errorType OnQuit(void** DS, const char* const command);


/***************************************************************************/
/* Parser                                                                  */
/***************************************************************************/

static errorType parser(const char* const command) { 
  static void *DS = NULL; /* The general data structure */
  const char* command_args = NULL;
  errorType rtn_val = error;

  commandType command_val = CheckCommand(command, &command_args);
 
  switch (command_val) {

	case (INIT_CMD):                rtn_val = OnInit(&DS, command_args);		break;
	case (HIRE_CMD):               	rtn_val = OnHire(DS, command_args);			break;
	case (FIRE_CMD):               	rtn_val = OnFire(DS, command_args);			break;
	case (GETEMPLOYER_CMD):         rtn_val = OnGetEmployer(DS, command_args);	break;
	case (GETSALARY_CMD):        	rtn_val = OnGetSalary(DS, command_args);	break;
	case (MEDIANSALARY_CMD):     	rtn_val = OnMedianSalary(DS, command_args);	break;
	case (HIGHESTSALARY_CMD):		rtn_val = OnHighestSalary(DS, command_args);break;
	case (UPDATESALARIES_CMD):		rtn_val = OnUpdateSalaries(DS, command_args);break;
	case (MERGER_CMD):				rtn_val = OnMerger(DS, command_args);		break;
	case (QUIT_CMD):                rtn_val = OnQuit(&DS, command_args);		break;
	
	case (COMMENT_CMD):             rtn_val = error_free;	break;
	case (NONE_CMD):                rtn_val = error;		break;
	default: assert(false);
  };
  return(rtn_val);
}





/***************************************************************************/
/* OnInit                                                                  */
/***************************************************************************/
static errorType OnInit(void** DS, const char* const command) {
	if(isInit) {
		printf("Init was already called.\n");
		return(error_free);
	};
	isInit = true;

	int	N;	
	ValidateRead( sscanf(command, "%d" ,&N), 1, "Init failed.\n" );
		
	*DS = Init(N);
	if( *DS == NULL ) {
		printf("Init failed.\n");
		return(error);
	};
	printf("Init done.\n");

	return error_free;
}


/***************************************************************************/
/* OnHire                                                             */
/***************************************************************************/
static errorType OnHire(void* DS, const char* const command) {
	int id;
	int company;
	int sal[2];
	ValidateRead( sscanf(command, "%d,%d,%d.%d",&id,&company,&sal[0], &sal[1]), 4, "Hire failed.\n" );
	if ( sal[1] >= 100 || sal[1] < 0 ) {
		printf("Updates should be written to 2 decimal places."); return error;
	}
	Rational salary((long long)sal[0] * 10 + sal[1], (long long)10);
	StatusType res = Hire(DS,id, company, salary);

	printf("Hire: %s\n", ReturnValToStr(res));

	return error_free;
}


/***************************************************************************/
/* OnFire                                                            */
/***************************************************************************/
static errorType OnFire(void* DS, const char* const command) {
	int id;
	int company;
	ValidateRead( sscanf(command, "%d,%d",&id,&company), 2, "Fire failed.\n" );
	
	StatusType res = Fire(DS, id, company);

	printf("Fire: %s\n", ReturnValToStr(res));

	return error_free;
}


/***************************************************************************/
/* OnGetEmployer                                                          */
/***************************************************************************/
static errorType OnGetEmployer(void* DS, const char* const command) {

	int id;
	ValidateRead( sscanf(command, "%d",&id), 1, "GetEmployer failed.\n" );

	int company;
	StatusType res = GetEmployer(DS,id,&company);
	
	if (res != SUCCESS) {
		printf("GetEmployer: %s\n",ReturnValToStr(res));
	}
	else {
		printf("GetEmployer: %s %d\n", ReturnValToStr(res),company);
	}

	return error_free;
}


/***************************************************************************/
/* OnGetSalary                                                      */
/***************************************************************************/
static errorType OnGetSalary(void* DS, const char* const command) {
	int id;
	ValidateRead( sscanf(command, "%d",&id), 1, "GetSalary failed.\n" );

	Rational salary;
	StatusType res = GetSalary(DS,id,&salary);
	
	if (res != SUCCESS) {
		printf("GetSalary: %s\n",ReturnValToStr(res));
	}
	else {
		std::ostringstream stream;
		stream << setprecision(2) << salary;
		std::string str =  stream.str();
		printf("GetSalary: %s %s\n", ReturnValToStr(res),str.c_str());
	}

	return error_free;
}


/***************************************************************************/
/* OnMedianSalary                                                   */
/***************************************************************************/
static errorType OnMedianSalary(void* DS, const char* const command) {
	int	company;
	ValidateRead( sscanf(command, "%d",&company), 1, "MedianSalary failed.\n" );
	Rational salary;
	StatusType res = MedianSalary(DS,company,&salary);
	
	if (res != SUCCESS) {
		printf("MedianSalary: %s\n",ReturnValToStr(res));
	}
	else {
		std::ostringstream stream;
		stream << setprecision(2) << salary;
		std::string str =  stream.str();
		printf("MedianSalary: %s, %s\n", ReturnValToStr(res),str.c_str());
	}

	return error_free;
}


/***************************************************************************/
/* OnHighestSalary                                        */
/***************************************************************************/
static errorType OnHighestSalary(void* DS, const char* const command) {
	int	company;
	ValidateRead( sscanf(command, "%d",&company), 1, "HighestSalary failed.\n" );

	Rational salary;
	StatusType res = HighestSalary(DS,company,&salary);
	
	if (res != SUCCESS) {
		printf("HighestSalary: %s\n",ReturnValToStr(res));
	}
	else {
		std::ostringstream stream;
		stream << setprecision(2) << salary;
		std::string str =  stream.str();
		printf("HighestSalary: %s, %s\n", ReturnValToStr(res),str.c_str());
	}

	return error_free;
}


/***************************************************************************/
/* OnIncreaseSalaries                                            */
/***************************************************************************/
static errorType OnUpdateSalaries(void* DS, const char* const command) {
	int	company;
	int u[2];
	ValidateRead( sscanf(command, "%d,%d.%d",&company,&u[0],&u[1]), 3, "UpdateSalaries failed.\n" );
	if ( u[1] >= 100 || u[1] < 0 ) {
		printf("Updates should be written to 2 decimal places."); return error;
	}
	Rational update(u[0] * 10 + u[1], 10);
	StatusType res = UpdateSalaries(DS,company, update);
	
	printf("UpdateSalaries: %s\n", ReturnValToStr(res));

	return error_free;
}


/***************************************************************************/
/* OnMerger                                           */
/***************************************************************************/
static errorType OnMerger(void* DS, const char* const command) {
	int	company1, company2;
	ValidateRead( sscanf(command, "%d,%d",&company1,&company2), 2, "Merger failed.\n" );

	StatusType res = Merger(DS,company1, company2);

	printf("Merger: %s\n", ReturnValToStr(res));

	return error_free;
}


/***************************************************************************/
/* OnQuit                                                                  */
/***************************************************************************/
static errorType OnQuit(void** DS, const char* const command) {
	Quit(DS);
	if( *DS != NULL ) {
		printf("Quit failed.\n");
		return(error);
	};
	isInit = false;
	printf("Quit done.\n");

	return error_free;
}

